using System;
using System.Windows;
using System.Windows.Media.Media3D;

namespace ttg_internal.Utilities.UsageVisualizer.Other.Controls
{
    internal class Cylinder
    {
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        public Cylinder()
        {
            height = 2.0;
            longitude = 48;
            radius = 1.0;
            changed = true;
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        public double Height
        {
            set
            {
                if (value != height)
                {
                    height = value;
                    changed = true;
                }
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        public int Longitude
        {
            set
            {
                if (value != longitude)
                {
                    longitude = value;
                    changed = true;
                }
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        public double Radius
        {
            set
            {
                if (value != radius)
                {
                    radius = value;
                    changed = true;
                }
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        public MeshGeometry3D Mesh
        {
            get
            {
                if (changed)
                {
                    CreateMesh();
                    changed = false;
                }

                return mesh;
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        private void CreateMesh()
        {
            mesh = new MeshGeometry3D();

            Point3D origin = new Point3D(0, 0, 0);
            Vector3D negY = new Vector3D(0, -1, 0);
            Vector3D posY = new Vector3D(0, 1, 0);
            Point texCoordTop = new Point(0, 0);
            Point texCoordBottom = new Point(0, 1);
            double lonDeltaTheta = 2.0 * Math.PI / (double)longitude;
            double y0 = height / 2;
            double y1 = -y0;
            double lonTheta = Math.PI;
            ushort indices = 0;

            for (int lon = 0; lon < longitude; lon++)
            {
                double u0 = (double)lon / (double)longitude;
                double x0 = radius * Math.Cos(lonTheta);
                double z0 = radius * Math.Sin(lonTheta);

                if (lon == longitude - 1)
                {
                    lonTheta = Math.PI;
                }
                else
                {
                    lonTheta -= lonDeltaTheta;
                }

                double u1 = (double)(lon + 1) / (double)longitude;
                double x1 = radius * Math.Cos(lonTheta);
                double z1 = radius * Math.Sin(lonTheta);
                Point3D p0 = new Point3D(x0, y1, z0);
                Point3D p1 = new Point3D(x0, y0, z0);
                Point3D p2 = new Point3D(x1, y1, z1);
                Point3D p3 = new Point3D(x1, y0, z1);
                Vector3D norm0 = new Vector3D(x0, 0, z0);
                Vector3D norm1 = new Vector3D(x1, 0, z1);

                norm0.Normalize();
                norm1.Normalize();

                // The triangles on the lateral face
                mesh.Positions.Add(p0);
                mesh.Positions.Add(p1);
                mesh.Positions.Add(p2);
                mesh.Positions.Add(p3);
                mesh.Normals.Add(norm0);
                mesh.Normals.Add(norm0);
                mesh.Normals.Add(norm1);
                mesh.Normals.Add(norm1);
                mesh.TextureCoordinates.Add(new Point(u0, 1));
                mesh.TextureCoordinates.Add(new Point(u0, 0));
                mesh.TextureCoordinates.Add(new Point(u1, 1));
                mesh.TextureCoordinates.Add(new Point(u1, 0));
                mesh.TriangleIndices.Add(indices++);  // 0
                mesh.TriangleIndices.Add(indices++);  // 1
                mesh.TriangleIndices.Add(indices++);  // 2
                mesh.TriangleIndices.Add(indices--);  // 3
                mesh.TriangleIndices.Add(indices--);  // 2
                mesh.TriangleIndices.Add(indices++);  // 1
                indices += 2;

                // The triangle at the base
                mesh.Positions.Add(p0);
                mesh.Positions.Add(p2);
                mesh.Positions.Add(new Point3D(0, y1, 0));
                mesh.Normals.Add(negY);
                mesh.Normals.Add(negY);
                mesh.Normals.Add(negY);
                mesh.TextureCoordinates.Add(texCoordBottom);
                mesh.TextureCoordinates.Add(texCoordBottom);
                mesh.TextureCoordinates.Add(texCoordBottom);
                mesh.TriangleIndices.Add(indices++);
                mesh.TriangleIndices.Add(indices++);
                mesh.TriangleIndices.Add(indices++);

                // The triangle at the base
                mesh.Positions.Add(p3);
                mesh.Positions.Add(p1);
                mesh.Positions.Add(new Point3D(0, y0, 0));
                mesh.Normals.Add(posY);
                mesh.Normals.Add(posY);
                mesh.Normals.Add(posY);
                mesh.TextureCoordinates.Add(texCoordTop);
                mesh.TextureCoordinates.Add(texCoordTop);
                mesh.TextureCoordinates.Add(texCoordTop);
                mesh.TriangleIndices.Add(indices++);
                mesh.TriangleIndices.Add(indices++);
                mesh.TriangleIndices.Add(indices++);
            }
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        private double height;

        private int longitude;

        private double radius;

        private bool changed;

        private MeshGeometry3D mesh;
    }
}
